/**
 ███████ ██    ██ ███    ██  ██████ ████████ ██  ██████  ███    ██
 ██      ██    ██ ████   ██ ██         ██    ██ ██    ██ ████   ██
 █████   ██    ██ ██ ██  ██ ██         ██    ██ ██    ██ ██ ██  ██
 ██      ██    ██ ██  ██ ██ ██         ██    ██ ██    ██ ██  ██ ██
 ██       ██████  ██   ████  ██████    ██    ██  ██████  ██   ████
 */


/**
 * 将字符串函数转化为js格式的字符串函数
 * @param string {String}
 */
function replaceFuncStr(string) {
    let mathFuncReplace = {
        "sin(": "Math.sin(",
        "cos(": "Math.cos(",
        "min(": "Math.min(",
        "max(": "Math.max(",
        "abs(": "Math.abs(",
        "tan(": "Math.tan(",
        "floor(": "Math.floor(",
        "ceil(": "Math.ceil(",
        "random(": "Math.random(",
        "ln(": "Math.log(",
        "arctan(": "Math.atan(",
        "arccos(": "Math.asin(",
        "arcsin(": "Math.acos(",
        "e": "Math.E",
        "pi": "Math.PI",
    };
    string = string.replaceAll(" ", "");
    for (let key in mathFuncReplace) {
        string = string.replaceAll(key, mathFuncReplace[key]);
    }
    return string;
}

/**
 * 计算一个函数的定积分
 * @param f 函数对象
 * @param a 积分下限
 * @param b 积分上限
 * @param barLen 每一个积分小细条的宽度
 */
function integral(f, a, b, barLen = 0.001) {
    let res = 0;
    let curX = a;
    while (curX < b) {
        res += f(curX) * barLen;
        curX += barLen;
    }
    return res;
}

/**
 * 求一个函数在某一点处的导数
 * @param f 函数对象
 * @param x 某点
 * @param dx 利用极限求导数，dx趋近于0
 * @return  {number[]} 返回一个长度2的数组，表示左导数和右导数
 */
function derivation(f, x, dx = 0.00001) {
    let right = (f(x + dx) - f(x)) / dx;
    let left = (f(x) - f(x - dx)) / dx;
    return [left, right];
}

/**
 * 求一个函数在某点的极限
 * @param f
 * @param x
 * @param dx
 * @return {number[]} 返回一个长度2的数组，表示左极限、右极限
 */
function limit(f, x, dx = 0.00000001) {
    let left = f(x - dx);
    let right = f(x + dx);
    return [left, right];
}

/**
 * ███    ███  █████  ████████ ██████  ██ ██   ██
 * ████  ████ ██   ██    ██    ██   ██ ██  ██ ██
 * ██ ████ ██ ███████    ██    ██████  ██   ███
 * ██  ██  ██ ██   ██    ██    ██   ██ ██  ██ ██
 * ██      ██ ██   ██    ██    ██   ██ ██ ██   ██
 */


/**
 * 生成子矩阵
 * @param arr2d 二维数组
 * @param i 去掉i行，i从0开始的
 * @param j 去掉j行，j从0开始的
 */
function subDec(arr2d, i, j) {
    let resDec = [];
    for (let y = 0; y < arr2d.length; y++) {
        if (y === i) {
            continue
        }
        let line = [];
        for (let x = 0; x < arr2d[0].length; x++) {
            if (x === j) {
                continue
            }
            line.push(arr2d[y][x]);
        }
        resDec.push(line);
    }
    return resDec;
}

/**
 * 计算一个n阶行列式的值
 * @param arr2d
 */
function dec(arr2d) {
    let n = arr2d.length;
    if (n === 1) {
        return arr2d[0][0];
    }
    if (n === 2) {
        return dec2(arr2d);
    } else {
        // 按第一行展开
        let res = 0;
        for (let x = 0; x < arr2d.length; x++) {
            // 生成余子式
            let a = x % 2 === 0 ? 1 : -1;
            let A = dec(subDec(arr2d, 0, x)) * a;
            res += arr2d[0][x] * A;
        }
        return res;
    }
}

/**
 * 计算一个二阶行列式的值
 */
function dec2(arr2d) {
    return arr2d[0][0] * arr2d[1][1] - arr2d[0][1] * arr2d[1][0];
}

/**
 * 转置一个矩阵
 * @param arr2d
 */
function matrixT(arr2d) {
    let res = [];
    for (let x = 0; x < arr2d[0].length; x++) {
        let line = [];
        for (let y = 0; y < arr2d.length; y++) {
            line.push(arr2d[y][x]);
        }
        res.push(line);
    }
    return res;
}

/**
 * 矩阵的每一个元素都乘以K
 * @param arr2d
 * @param k
 */
function matrixK(arr2d, k) {
    for (let y = 0; y < arr2d.length; y++) {
        for (let x = 0; x < arr2d[0].length; x++) {
            arr2d[y][x] *= k;
        }
    }
    return arr2d;
}

/**
 * 求逆矩阵
 * @param arr2d
 */
function matrixR(arr2d) {
    return matrixK(matrixS(arr2d), 1 / dec(arr2d));
}

/**
 * 求伴随矩阵
 * @param arr2d
 */
function matrixS(arr2d) {
    let res = [];
    for (let y = 0; y < arr2d.length; y++) {
        let line = [];
        for (let x = 0; x < arr2d[0].length; x++) {
            line.push(dec(subDec(arr2d, y, x)) * ((x + y) % 2 === 0 ? 1 : -1));
        }
        res.push(line);
    }
    return matrixT(res);
}

/**
 * 矩阵乘法
 * @param arr1
 * @param arr2
 */
function matrixMul(arr1, arr2) {
    let height = arr1.length;
    let width = arr1[0].length;
    let res = []
    let resL = height;
    for (let lineI = 0; lineI < resL; lineI++) {
        // 当前是lineI 行
        let resLine = [];
        for (let colmI = 0; colmI < resL; colmI++) {
            // 当前是colmI列
            let ans = 0;
            for (let i = 0; i < width; i++) {
                let n1 = arr1[lineI][i];
                let n2 = arr2[i][colmI];
                ans += n1 * n2;
            }
            resLine.push(ans);
        }
        res.push(resLine);
    }

    return res;
}

function div(innerText, className = "") {
    let res = document.createElement("div");
    res.innerHTML = innerText;
    if (className !== "") {
        res.classList.add(className);
    }
    return res;
}

function printMatrix(arr2d) {
    for (let line of arr2d) {
        console.log(line);
    }
    console.log();
}

/**
 * 生成全是0的方阵
 * @param N
 */
function zeros(N) {
    let res = [];
    for (let y = 0; y < N; y++) {
        let line = [];
        for (let x = 0; x < N; x++) {
            line.push(0);
        }
        res.push(line);
    }
    return res;
}

/**
 * 检测一个字符串是不是int
 */
function strIsInt(token) {
    for (let char of token) {
        if (!"1234567890".includes(char)) {
            return false;
        }
    }
    return true;
}

//中缀表达式转后缀表达式
class InfixToSuffix {

    /**
     * 中缀表达式转后缀表达式主方法
     * @param expression  "(10+20/2*3)/2+8"  可以随便加空格的
     * @return {string} "10 20 2 / 3 * + 2 / 8 +"
     */
    static run(expression) {
        //操作符的栈
        let opStack = [];
        //后缀表达式的线性表
        let suffixList = [];

        //格式化表达式
        expression = this.insertBlanks(expression);
        let tokens = expression.split(" ");
        for (let token of tokens) {
            //过滤空串
            if (token.length === 0) {
                continue;
            }
            //判断操作符+ - * /
            if (this.isOperator(token)) {
                /*
                    什么时候操作符进栈？
                    1.如果栈为空
                    2.如果栈顶是 (
                    3.如果栈顶是操作符，且优先级比当前token小
                    什么时候需要将栈顶操作符出栈？
                    1.栈顶操作符的优先级 >= 当前token
                */
                while (true) {
                    if (
                        opStack.length === 0 ||
                        opStack[opStack.length - 1] === "(" ||
                        this.priority(opStack[opStack.length - 1]) < this.priority(token)
                    ) {
                        opStack.push(token);
                        break;
                    }
                    suffixList.push(opStack.pop());
                }
            } else if (token === ("(")) {
                opStack.push(token);
            } else if (token === (")")) {
                while (!(opStack[opStack.length - 1] === "(")) {
                    suffixList.push(opStack.pop());
                }
                opStack.pop();
            } else if (strIsInt(token)) {
                suffixList.push(token);
            } else {
                console.log("IllegalArgumentException");
                return "";
            }
        }
        while (opStack.length !== 0) {
            suffixList.push(opStack.pop());
        }
        //将数字元素和操作符元素进行拼接
        let sb = "";
        for (let i = 0; i < suffixList.length; i++) {
            sb += suffixList[i];
            sb += ' ';
        }
        return sb.toString();
    }

    static priority(token) {
        if (token === "+" || token === "-") {
            return 0;
        }
        if (token === "*" || token === "/") {
            return 1;
        }
        return -1;
    }

    static isOperator(token) {
        return token === "+" || token === "-" || token === "*" || token === "/";
    }

    //对原表达式进行格式化处理 给所有的非数字字符两边添加空格
    static insertBlanks(expression) {
        let sb = "";
        for (let i = 0; i < expression.length; i++) {
            let c = expression[i];
            if (c === '(' || c === ')' || c === '+' || c === '-' || c === '*' || c === '/') {
                sb += ` ${c} `;
            } else {
                sb += c;
            }
        }
        return sb.toString();
    }
}

